use std::{collections::BTreeMap, fs::{read_dir, File}, io::{copy, Read, Write}, path::{Path, PathBuf}, process::{Command, Stdio}};

#[cfg(feature = "zi")]
use std::{fs::{create_dir_all, remove_dir_all}, path::absolute};

#[cfg(unix)]
use std::{fs, os::unix::fs::PermissionsExt};

#[cfg(target_os = "windows")]
use std::os::windows::process::CommandExt;

#[cfg(feature = "reg")]
use regex::Regex;
#[cfg(feature = "ser")]
use serde::{de::DeserializeOwned, Serialize};
#[cfg(feature = "zi")]
use zip::{write::SimpleFileOptions, ZipArchive, ZipWriter};

#[cfg(feature = "ser")]
use crate::{
    app::{get_wtask_data_dir, get_wtask_root_dir},
    logs::WTASK_BASIC_FILE_NAME,
    encrypt::{decrypt_free, encrypt_free}
};

use super::error::{WError, WResult};


#[cfg(feature = "reg")]
pub fn file_name_remove_space(data: &str) -> String {
    let re_zh = Regex::new(r"[\p{Han}]").unwrap();
    let re = Regex::new(r"[\w\.-]+").unwrap();
    let data = re_zh.replace_all(data, "").into_owned();
    re.find_iter(&data)
        .map(|mat| mat.as_str())
        .collect::<Vec<&str>>()
        .join(".")
}




pub fn run_cmd_pure(mut cmd: Vec<&str>, envs: Option<BTreeMap<&str, &str>>, dir: Option<&Path>) -> WResult<String> {
    let program = cmd.drain(0..1).collect::<String>();
    
    let mut child = Command::new(program);
    if !cmd.is_empty() {
        child.args(&cmd);
    }
    if let Some(envs) = envs {
        child.envs(&envs);
    }
    if let Some(dir) = dir {
        child.current_dir(dir);
    }

    match child.output() {
        Ok(opt) => Ok(String::from_utf8_lossy(&opt.stdout).trim().to_owned()),
        Err(e) => Err(WError::DataError(format!("{e}")))
    }
}


pub fn read_file(f: &Path) -> WResult<String> {
    let mut res = String::new();
    let mut f = File::open(f)?;
    f.read_to_string(&mut res)?;

    res = res.trim().to_owned();

    Ok(res)
}


pub fn create_file(f: &Path, content: &str) -> WResult<()> {
    let mut f = File::create(f)?;
    f.write_all(content.as_bytes())?;
    Ok(())
}




#[allow(unused_variables)]
pub fn file_to_x_power(f: &Path) -> WResult<()> {
    #[cfg(unix)]
    {
        // 创建或打开文件
        let file = File::open(f)?;
    
        // 获取当前权限
        let metadata = file.metadata()?;
        let mut permissions = metadata.permissions();
    
        // 设置新的权限。0o755表示所有者可读、写及执行；组用户和其他人可读及执行。
        // 注意：这里仅展示了添加执行权限的一种方式，实际权限应根据需求调整。
        permissions.set_mode(0o700);
    
        // 应用新权限
        fs::set_permissions(f, permissions)?;
    }
    Ok(())
}


pub fn file_cp(f1: &Path, f2: &Path) -> WResult<()> {
    let mut file = File::open(f1)?;
    let mut outfile = File::create(f2)?;
    copy(&mut file, &mut outfile)?;
    Ok(())
}


fn dir_files(dir: &Path) -> WResult<Vec<PathBuf>> {
    let mut res = Vec::new();
    if dir.is_dir() {
        for entry in read_dir(dir)? {
            let p = entry?.path();
            if p.is_dir() {
                let res_temp = dir_files(&p)?;
                res.extend(res_temp);
            } else {
                res.push(p);
            }
        }
    }
    Ok(res)
}


#[cfg(feature = "zi")]
pub fn file_zip(dir: &Path, f: &Path) -> WResult<()> {
    let file = File::create(f)?;
    let mut zip = ZipWriter::new(file);
    let options = SimpleFileOptions::default()
        .compression_method(zip::CompressionMethod::Deflated)
        .unix_permissions(0o700);

    let prefix = absolute(dir)?.to_string_lossy().to_string();

    let mut buffer = Vec::new();
    for file_single in dir_files(dir)? {
        let name = file_single.strip_prefix(&prefix).unwrap().to_string_lossy().to_string();
        zip.start_file(name, options)?;

        let mut f = File::open(file_single)?;
        f.read_to_end(&mut buffer)?;
        zip.write_all(&buffer)?;
        buffer.clear();
    }

    zip.finish()?;

    Ok(())
}

#[cfg(feature = "zi")]
pub fn file_unzip(f: &Path, dir: &Path) -> WResult<()> {
    let file = File::open(f)?;
    let mut archive = ZipArchive::new(file)?;

    for i in 0..archive.len() {
        let mut file = archive.by_index(i)?;
        let outpath = match file.enclosed_name() {
            Some(path) => dir.join(path),
            None => continue,
        };

        if file.is_dir() {
            let _ = remove_dir_all(&outpath);
            create_dir_all(&outpath)?;
        } else {
            if let Some(p) = outpath.parent()
                && !p.exists() {
                    create_dir_all(p)?;
                }
            let mut outfile = File::create(&outpath)?;
            copy(&mut file, &mut outfile)?;
        }

        // Get and Set permissions
        #[cfg(unix)]
        {
            if let Some(mode) = file.unix_mode() {
                fs::set_permissions(&outpath, fs::Permissions::from_mode(mode))?;
            }
        }
    }
    Ok(())
}


#[cfg(feature = "ser")]
fn dir_en_de<P: AsRef<Path>>(file_name: P, dir: Option<(&str, bool)>, ext_append: bool) -> WResult<PathBuf> {
    let mut p = if let Some((tag, use_date)) = dir {
        get_wtask_data_dir(tag, use_date)?
    } else {
        get_wtask_root_dir()
    }.join(file_name);
    if ext_append {
        let ext = if let Some(ext) = p.extension() {
            format!("{}.{}", ext.to_str().unwrap_or_default(), WTASK_BASIC_FILE_NAME)
        } else {
            WTASK_BASIC_FILE_NAME.to_string()
        };
        p.set_extension(ext);
    }
    Ok(p)
}


#[cfg(feature = "ser")]
pub fn file_de_load<T: DeserializeOwned, P: AsRef<Path>>(file_name: P, dir: Option<(&str, bool)>, ext_append: bool) -> WResult<T> {
    let p = dir_en_de(file_name, dir, ext_append)?;
    let mut f = File::open(p)?;
    let mut data = Vec::new();
    f.read_to_end(&mut data)?;
    let data = decrypt_free(&data)?;
    let r = serde_json::from_slice(&data)?;
    Ok(r)
}


#[cfg(feature = "ser")]
pub fn file_en_save<T: Serialize, P: AsRef<Path>>(file_name: P, data: &T, dir: Option<(&str, bool)>) -> WResult<PathBuf> {
    let data = serde_json::to_vec(data)?;
    let data = encrypt_free(&data)?;
    let p = dir_en_de(file_name, dir, true)?;
    let mut f = File::create(&p)?;
    f.write_all(&data)?;
    Ok(p)
}




#[cfg(unix)]
pub fn start_exe_spawn(dir: &Path, args: Vec<String>) -> WResult<()> {
    log::debug!("exe spawn: {args:?}");
    let _child = Command::new("sh")
        .args(["-c", &args.join(" ")])
        .current_dir(dir)
        .stdout(Stdio::null()) // 分离标准输出
        .stderr(Stdio::null()) // 分离标准错误
        .stdin(Stdio::null())  // 分离标准输入
        .spawn()?;
    Ok(())
}


#[cfg(target_os = "windows")]
pub fn start_exe_spawn(dir: &Path, mut args: Vec<String>) -> WResult<()> {
    let p = args.drain(0..1).collect::<Vec<_>>().pop().unwrap();
    const DETACHED_PROCESS: u32 = 0x00000008;
    let mut binding = Command::new(format!("{}.exe", p));
    let cmd = binding.creation_flags(DETACHED_PROCESS);

    if !args.is_empty() {
        cmd.args(args);
    }
    
    let _child = cmd
        .current_dir(dir)
        .stdout(Stdio::null()) // 分离标准输出
        .stderr(Stdio::null()) // 分离标准错误
        .stdin(Stdio::null())  // 分离标准输入
        .spawn()?;
    Ok(())
}